﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text;
using uTorrentNetClient.ModelDesign.Helper;
using System.Collections.ObjectModel;
using Microsoft.Practices.Composite.Presentation.Commands;
using uTorrentNetClient.ModelDesign.Contracts;
using Cleverscape.UTorrentClient.WebClient;
using Microsoft.Practices.Composite.Events;
using uTorrentNetClient.ModelDesign.Events;
using uTorrentNetClient.ModelDesign.OptionalConverter;

namespace uTorrentNetClient.ModelDesign.Settings.Model
{
    public class SettingsModel : Base, ISettingsModel
    {
        private IEventAggregator _eventAggregator;
        private List<SettingsGroupMapping> _settingsGroupMapping;

        public ISettingsView View { get; set; }


        private bool _showDialog;
        public bool ShowDialog
        {
            get { return _showDialog; }
            set
            {
                _showDialog = value;
                SetNotifyPropertyChanged("ShowDialog");
            }
        }

        public DelegateCommand<object> OkCommand { get; set; }
        public DelegateCommand<object> CancelCommand { get; set; }
        public DelegateCommand<object> EditSettingsCommand { get; set; }

        private ObservableCollection<SettingsGroupHelper> _settingsGroupCollection;
        public ObservableCollection<SettingsGroupHelper> SettingsGroupCollection
        {
            get { return _settingsGroupCollection; }
            set
            {
                _settingsGroupCollection = value;
                SetNotifyPropertyChanged("SettingsGroupCollection");
            }
        }

        private SettingsGroupHelper _selectedGroup;
        public SettingsGroupHelper SelectedGroup
        {
            get { return _selectedGroup; }
            set
            {
                _selectedGroup = value;
                SetNotifyPropertyChanged("SelectedGroup");
            }
        }

        private AppSetting _appSettings;
        public AppSetting AppSettings
        {
            get { return _appSettings; }
            set
            {
                _appSettings = value;
                SetNotifyPropertyChanged("AppSettings");
            }
        }

        public UTorrentWebClient WebClient { get; set; }

        public SettingsModel(ISettingsView view, IEventAggregator eventAggregator)
        {
            View = view;
            View.DataContext = this;

            _eventAggregator = eventAggregator;

            //_settingsGroupMapping = new List<SettingsGroupMapping>();
            loadSettingsMapping();

            OkCommand = new DelegateCommand<object>(okCommandExecute);
            CancelCommand = new DelegateCommand<object>(cancelCommandExecute);
            EditSettingsCommand = new DelegateCommand<object>(editSettingsCommandExecute);
        }



        public void SetGroups()
        {
            SettingsGroupCollection = new ObservableCollection<SettingsGroupHelper>();

            if (AppSettings != null)
            {

                var group = new SettingsGroupHelper { Group = "Anwendung", Position = 0 };
                group.Settings.Add(new SettingsHelper { Key = "Server", Name = "Server", Value = AppSettings.Server, ValueType = SettingsValueType.Text, Position = 0});
                group.Settings.Add(new SettingsHelper { Key = "Port", Name = "Port", Value = AppSettings.Port, ValueType = SettingsValueType.Integer, Position = 1});
                group.Settings.Add(new SettingsHelper { Key = "User", Name = "Benutzer", Value = AppSettings.User, ValueType = SettingsValueType.Text, Position = 2});
                group.Settings.Add(new SettingsHelper { Key = "Password", Name = "Passwort", Value = AppSettings.Password, ValueType = SettingsValueType.Password, Position = 3});
                group.Settings.Add(new SettingsHelper { Key = "RefreshSec", Name = "Aktualiesierung alle x Sec", Value = AppSettings.RefreshSec, ValueType = SettingsValueType.Integer, Position = 4});
                group.Settings.Add(new SettingsHelper { Key = "ShowNotifyWindow", Name = "Chart anzeigen", Value = AppSettings.ShowNotifyWindow, ValueType = SettingsValueType.Bool, Position = 5});
                group.Settings.Add(new SettingsHelper { Key = "ShowDialogOnNewTorrent", Name = "Einstellungen beim neuen Torrent anzeigen", Value = AppSettings.ShowDialogOnNewTorrent, ValueType = SettingsValueType.Bool, Position = 6});
                group.FromApp = true;

                SettingsGroupCollection.Add(group);

                if (WebClient != null)
                {
                    foreach (var setting in WebClient.Settings)
                    {
                        var mapping = _settingsGroupMapping.FirstOrDefault(x => x.Key == setting.Name);

                        if (mapping == null)
                        {
                            var g = SettingsGroupCollection.FirstOrDefault(x => x.Group == "Undefiniert");
                            if (g == null)
                            {
                                g = new SettingsGroupHelper { Group = "Undefiniert", Position = 1001 };
                                SettingsGroupCollection.Add(g);
                            }

                            var position = 0;

                            if (g.Settings.Count > 0)
                                position = g.Settings.Max(x => x.Position);

                            var valueType = getValueType(setting, ConverterType.Kein, SettingsValueType.Text);

                            g.Settings.Add(new SettingsHelper
                                               {
                                                   Key = setting.Name,
                                                   Name = setting.Name,
                                                   Position = position + 1,
                                                   Value = getValue(setting, ConverterType.Kein),
                                                   ValueType = valueType,
                                                   Converter = ConverterType.Kein
                                               });

                            _settingsGroupMapping.Add(new SettingsGroupMapping(g.Group, g.Position, setting.Name, position, setting.Name, false, ConverterType.Kein, valueType));
                        }
                        else
                        {
                            if (!mapping.Hide)
                            {
                                var g = SettingsGroupCollection.FirstOrDefault(x => x.Group == mapping.Group);
                                if (g == null)
                                {
                                    g = new SettingsGroupHelper { Group = mapping.Group, Position = mapping.GroupPosition };
                                    SettingsGroupCollection.Add(g);
                                }

                                g.Settings.Add(new SettingsHelper
                                                   {
                                                       Key = setting.Name,
                                                       Name = mapping.Name,
                                                       Position = mapping.Position,
                                                       Value = getValue(setting, mapping.Converter),
                                                       ValueType = getValueType(setting, mapping.Converter, mapping.ValueType),
                                                       Converter = mapping.Converter
                                                   });
                            }
                        }
                    }
                }

                resetChanges();

                SelectedGroup = group;
            }
        }

        private void setAppSettingNewValues()
        {
            var conGroup = SettingsGroupCollection.FirstOrDefault(x => x.FromApp && x.Group == "Anwendung");

            if (conGroup != null)
            {
                foreach (var setting in conGroup.Settings)
                {
                    switch (setting.Key)
                    {
                        case "Server":
                            if (setting.HasChanges)
                                AppSettings.Server = setting.Value as string;
                            break;

                        case "Port":
                            if (setting.HasChanges)
                            {
                                var value = AppSettings.Port;
                                int.TryParse(setting.Value.ToString(), out value);
                                AppSettings.Port = value;
                            }
                            break;

                        case "User":
                            if (setting.HasChanges)
                                AppSettings.User = setting.Value as string;
                            break;

                        case "Password":
                            if (setting.HasChanges)
                                AppSettings.Password = setting.Value as string;
                            break;

                        case "RefreshSec":
                            if (setting.HasChanges)
                            {
                                var value = AppSettings.RefreshSec;
                                int.TryParse(setting.Value.ToString(), out value);
                                AppSettings.RefreshSec = value;
                            }
                            break;

                        case "ShowNotifyWindow":
                            if (setting.HasChanges)
                            {
                                var value = AppSettings.ShowNotifyWindow;
                                bool.TryParse(setting.Value.ToString(), out value);
                                AppSettings.ShowNotifyWindow = value;
                            }
                            break;

                        case "ShowDialogOnNewTorrent":
                            if (setting.HasChanges)
                            {
                                var value = AppSettings.ShowDialogOnNewTorrent;
                                bool.TryParse(setting.Value.ToString(), out value);
                                AppSettings.ShowDialogOnNewTorrent = value;
                            }
                            break;

                        default:
                            break;
                    }
                }
            }

        }

        private void resetChanges()
        {
            foreach (var group in SettingsGroupCollection)
            {
                foreach (var setting in group.Settings)
                {
                    setting.ResetChanges();
                }
            }
        }

        private void loadSettingsMapping()
        {
            try
            {
                var path = Path.Combine(new ConfigurationHandling.ConfigRepository().SettingsPath, "SettingsGroupMapping.xml");

                if (File.Exists(path))
                    _settingsGroupMapping = FileHandling.FileHandler.LoadFromXML<List<SettingsGroupMapping>>(path);
                else
                    _settingsGroupMapping = new List<SettingsGroupMapping>();
            }
            catch (Exception ex)
            {
                _settingsGroupMapping = new List<SettingsGroupMapping>();
            }
        }

        private object getValue(SettingBase setting, ConverterType converter)
        {
            switch (setting.Type)
            {
                case SettingType.Integer:
                    var s1 = (SettingBase<int>)setting;
                    return useConverter(s1.Value, converter);

                case SettingType.String:
                    var s2 = (SettingBase<string>)setting;
                    return useConverter(s2.Value, converter);

                case SettingType.Boolean:
                    var s3 = (SettingBase<bool>)setting;
                    return useConverter(s3.Value, converter);
            }

            return null;
        }

        private object useConverter(object value, ConverterType converter)
        {
            switch (converter)
            {
                case ConverterType.Kein:
                    return value;

                case ConverterType.MinToHours:

                    int i = 0;
                    if (int.TryParse(value.ToString(), out i))
                        return MinToHoursConverter.Forward(i);
                    return value;

                case ConverterType.IntToBool:

                    int i1 = 0;
                    if (int.TryParse(value.ToString(), out i1))
                        return IntToBoolConverter.Forward(i1);
                    return value;

                case ConverterType.SecToHours:

                    int i2 = 0;
                    if (int.TryParse(value.ToString(), out i2))
                        return SecToHoursConverter.Forward(i2);
                    return value;

                default:
                    throw new ArgumentOutOfRangeException("converter");
            }
        }

        private object useConverterBack(object value, ConverterType converter)
        {
            switch (converter)
            {
                case ConverterType.Kein:
                    return value;

                case ConverterType.MinToHours:

                    int i = 0;
                    if (int.TryParse(value.ToString(), out i))
                        return MinToHoursConverter.Backward(i);
                    return value;

                case ConverterType.IntToBool:

                    bool b = false;
                    if (bool.TryParse(value.ToString(), out b))
                        return IntToBoolConverter.Backward(b);
                    return value;

                case ConverterType.SecToHours:

                    int i2 = 0;
                    if (int.TryParse(value.ToString(), out i2))
                        return SecToHoursConverter.Backward(i2);
                    return value;

                default:
                    throw new ArgumentOutOfRangeException("converter");
            }
        }

        private SettingsValueType getValueType(SettingBase setting, ConverterType converter, SettingsValueType valueTypeFromMapping)
        {
            switch (setting.Type)
            {
                case SettingType.Integer:
                    if (converter == ConverterType.IntToBool)
                        return SettingsValueType.Bool;
                    else
                        return SettingsValueType.Integer;
                case SettingType.String:
                    if (valueTypeFromMapping == SettingsValueType.Password)
                        return SettingsValueType.Password;
                    else
                        return SettingsValueType.Text;
                case SettingType.Boolean:
                    return SettingsValueType.Bool;
            }

            return SettingsValueType.Text;
        }

        private void saveSettings()
        {
            if (SettingsGroupCollection != null && WebClient != null)
            {
                foreach (var groupHelper in SettingsGroupCollection.Where(x => x.FromApp == false))
                {
                    foreach (var setting in groupHelper.Settings.Where(x => x.HasChanges))
                    {
                        if (setting.HasChanges)
                        {
                            var serverSetting = WebClient.Settings.FirstOrDefault(x => x.Name == setting.Key); ;
                            if (serverSetting != null)
                            {
                                switch (serverSetting.Type)
                                {
                                    case SettingType.Integer:
                                        var s1 = (SettingBase<int>)serverSetting;
                                        int value = s1.Value;
                                        int.TryParse(useConverterBack(setting.Value, setting.Converter).ToString(), out value);
                                        s1.Value = value;
                                        break;
                                    case SettingType.String:
                                        var s2 = (SettingBase<string>)serverSetting;
                                        s2.Value = useConverterBack(setting.Value, setting.Converter).ToString();
                                        break;
                                    case SettingType.Boolean:
                                        var s3 = (SettingBase<bool>)serverSetting;
                                        bool value2 = s3.Value;
                                        bool.TryParse(useConverterBack(setting.Value, setting.Converter).ToString(), out value2);
                                        s3.Value = value2;
                                        break;
                                }
                            }
                        }
                    }
                }
            }
        }

        #region Commands

        private void okCommandExecute(object o)
        {
            setAppSettingNewValues();
            saveSettings();
            var c = new CloseSettingsHelper { AppSetting = AppSettings, GroupMappings = null, ShowEditSettings = false };
            _eventAggregator.GetEvent<CloseSettingsEvent>().Publish(c);
        }

        private void cancelCommandExecute(object o)
        {
            _eventAggregator.GetEvent<CloseSettingsEvent>().Publish(null);
        }

        private void editSettingsCommandExecute(object obj)
        {
            setAppSettingNewValues();
            saveSettings();
            var c = new CloseSettingsHelper { AppSetting = AppSettings, GroupMappings = _settingsGroupMapping, ShowEditSettings = true };
            _eventAggregator.GetEvent<CloseSettingsEvent>().Publish(c);
        }

        #endregion
    }
}
